package com.apes.framework.plugin.express;

import com.apes.framework.api.SpringManager;
import com.ql.util.express.DynamicParamsUtil;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.IExpressContext;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class ExpressEngine implements IEngine<String, Map> {
    protected final Logger logger = Logger.getLogger(getClass());
    private Boolean isCache = true;
    protected boolean isInitialRunner = false;
    protected ExpressRunner runner;
    private List<String> funcAlias = new ArrayList<>();

    public ExpressEngine() {
        this.runner = new ExpressRunner(true, false);
        initRunner();
    }

    @Override
    public <T> T execute(String express, Map context) {
        IExpressContext expressContext;
        if (context instanceof IExpressContext) {
            expressContext = (IExpressContext) context;
        } else {
            expressContext = new ApesExpressContext(context);
        }
        express = initStatement(express);
        check(express, context);
        try {
            return (T) runner.execute(express, expressContext, null, isCache, false);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("表达式：'" + express + "'执行错误：" + e.getMessage());
        }
    }

    private void check(String express, Map context) {
        String[] names;
        try {
            names = runner.getOutVarNames(express);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("表达式：'" + express + "'错误：" + e.getMessage());
        }
        for (String name : names) {
            if (!context.containsKey(name.trim()) && !SpringManager.containsBean(name.trim())) {
                throw new RuntimeException("表达式：'" + express + "'没有上下文变量：'" + name + "'");
            }
        }
    }

    @Override
    public <T> T execute(String express, Map context, Function<Exception, String> func) {
        try {
            return execute(express, context);
        } catch (Exception e) {
            String message = func == null ? e.getMessage() : func.apply(e);
            throw new RuntimeException(message);
        }
    }

    @Override
    public <T> T execute(String express, Map context, T defult, Function<Exception, String> func) {
        try {
            return execute(express, context);
        } catch (Exception e) {
            //e.printStackTrace();
            //if (func != null) logger.warn(func.apply(e));
            return defult;
        }
    }

    /**
     * 在此处把一些中文符号替换成英文符号
     *
     * @param statement
     * @return
     */
    private String initStatement(String statement) {
        return statement.replace("（", "(").replace("）", ")").replace("；", ";").replace("，", ",").replace("“", "\"").replace("”", "\"");
    }

    @Override
    public void initRunner() {
        if (isInitialRunner || runner == null) {
            return;
        }
        synchronized (runner) {
            try {
                doInitRunner();
            } catch (Exception e) {
                throw new RuntimeException("初始化失败!", e);
            }
        }
        isInitialRunner = true;
    }

    protected void doInitRunner() throws Exception {
        DynamicParamsUtil.supportDynamicParams = true;
        addOperatorWithAlias();
        registerFuncAlias(SpringManager.getApplicationContext());
    }

    private void addOperatorWithAlias() throws Exception {
        runner.addOperatorWithAlias("如果", "if", null);
        runner.addOperatorWithAlias("则", "then", null);
        runner.addOperatorWithAlias("否则", "else", null);
        runner.addOperatorWithAlias("并且", "&&", null);
        runner.addOperatorWithAlias("或者", "||", null);
        runner.addOperatorWithAlias("等于", "==", null);
        runner.addOperatorWithAlias("不等于", "!=", null);
        runner.addOperatorWithAlias("小于", "<", null);
        runner.addOperatorWithAlias("小于等于", "<=", null);
        runner.addOperatorWithAlias("大于", ">", null);
        runner.addOperatorWithAlias("大于等于", ">=", null);
    }

    private void registerFuncAlias(final ApplicationContext context) {
        String[] beanNames = context.getBeanNamesForAnnotation(Component.class);
        for (final String beanName : beanNames) {
            Class<?> handlerType = context.getType(beanName);
            ReflectionUtils.doWithMethods(handlerType, method -> {
                ReflectionUtils.makeAccessible(method);
                FuncAlias annotation = AnnotationUtils.findAnnotation(method, FuncAlias.class);
                List<String> parameterTypes = Arrays.stream(method.getParameterTypes()).map(clazz -> clazz.getName()).collect(Collectors.toList());
                Object target = context.getBean(beanName);
                try {
                    if (funcAlias.contains(annotation.name())) {
                        logger.error("方法别名：" + annotation.name() + " 重复定义！");
                    } else {
                        runner.addFunctionOfServiceMethod(annotation.name(), target, method.getName(), parameterTypes.toArray(new String[parameterTypes.size()]), null);
                        funcAlias.add(annotation.name());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, method -> !method.isSynthetic() && AnnotationUtils.findAnnotation(method, FuncAlias.class) != null);
        }

        if (context.getParent() != null) {
            registerFuncAlias(context.getParent());
        }
    }

    public ExpressRunner getExpressRunner() {
        return runner;
    }

    public List<String> getFuncAlias() {
        return funcAlias;
    }
}
